package helper

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"
	
	"collecter/app/common/enum"
	"collecter/app/lang"
)

type UpFile struct {
	FilePointer multipart.File
	Headers     *multipart.FileHeader
	UploadType  string // 上传类型，文件 / 图片
	Exts        string // 允许上传的文件后缀名列表，逗号分隔
	FileSize    int64  // 允许上传的文件大小，单位字节
	SavePath    string // 上传的文件保存的路径，如果值为空则按照日期保存
	IsRename    bool   // 是否重命名文件
	IsThumbnail bool   // 是否生产缩略图（前期暂时不做这个）
}

type FileReturnType struct {
	Url          string `json:"url"`
	ThumbnailUrl string `json:"thumbnail_url"`
	SaveFilename string `json:"save_filename"`
}

var (
	// 允许上传的后缀名列表
	allowExts string
	// 允许上传的文件大小
	allowSize int64
	// 上传文件保存路径
	savePath string
	// 图片缩略图保存路径
	thumbnailPath string
	// 文件保存名称
	fileName string
	// 文件指纹
	// thumbnail_url string
)

func (this *UpFile) Upload() (FileReturnType, error) {
	var rst FileReturnType
	if this.FilePointer == nil || this.Headers == nil {
		return rst, errors.New("文件未上传")
	}
	// 设置上传相关参数
	this.setExts()
	this.setFileSize()
	this.setPath()
	this.setFileName()
	// 检查上传相关参数
	if err := this.checkExt(); err != nil {
		return rst, err
	}
	if err := this.checkFileSize(); err != nil {
		return rst, err
	}
	if err := this.checkPath(); err != nil {
		return rst, err
	}

	fillFileName := savePath + "/" + fileName
	f, err := os.OpenFile(fillFileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return rst, errors.New("打开目标文件出错。file: " + fillFileName + " errMsg: " + err.Error())
	}
	defer func() {
		_ = f.Close()
	}()
	if _, err := io.Copy(f, this.FilePointer); err != nil {
		return rst, errors.New("保存上传文件出错：" + err.Error())
	}
	rst.Url = fillFileName
	rst.SaveFilename = fileName
	return rst, nil
}

func (this *UpFile) setExts() {
	if len(this.Exts) > 0 {
		allowExts = this.Exts
	} else {
		switch this.UploadType {
		case enum.FILE_TYPE_IMAGE:
			// 图片允许的后缀名列表
			allowExts = Configer.GetString("upload_file.allow_upload_img_exts")
		case enum.FILE_TYPE_FILE:
			allowExts = Configer.GetString("upload_file.allow_upload_file_exts")
		case enum.FILE_TYPE_VIDEO:
			allowExts = Configer.GetString("upload_file.allow_upload_video_exts")
		case enum.FILE_TYPE_VOICES:
			allowExts = Configer.GetString("upload_file.allow_upload_voices_exts")
		case enum.FILE_TYPE_OTHER:
			allowExts = Configer.GetString("upload_file.allow_upload_other_file_exts")
		default:
			// 默认上传图片
			allowExts = Configer.GetString("upload_file.allow_upload_img_exts")
		}
	}
}

func (this *UpFile) setFileSize() {
	if this.FileSize > 0 {
		allowSize = this.FileSize
	} else {
		switch this.UploadType {
		case enum.FILE_TYPE_IMAGE:
			// 图片允许的文件大小，默认 5 M
			allowSize = Configer.GetInt64("upload_file.img_file_size_limit")
		case enum.FILE_TYPE_FILE:
			// 文件允许的文件大小，默认 20 M
			allowSize = Configer.GetInt64("upload_file.file_size_limit")
		case enum.FILE_TYPE_VIDEO:
			// 视频允许的文件大小，默认 100 M
			allowSize = Configer.GetInt64("upload_file.video_file_size_limit")
		case enum.FILE_TYPE_VOICES:
			// 音频允许的文件大小，默认 50 M
			allowSize = Configer.GetInt64("upload_file.voices_file_size_limit")
		case enum.FILE_TYPE_OTHER:
			// 其他文件允许的文件大小，默认 150 M
			allowSize = Configer.GetInt64("upload_file.other_file_size_limit")
		default:
			// 图片允许的文件大小，默认 5 M
			allowSize = Configer.GetInt64("upload_file.img_file_size_limit")
		}
	}
}

func (this *UpFile) setPath() {
	var baseBasePath string
	baseBasePath = "static/uploads/"
	switch this.UploadType {
	case enum.FILE_TYPE_IMAGE:
		savePath = baseBasePath + "images/"
		thumbnailPath = baseBasePath + "thumbnail/"
	case enum.FILE_TYPE_FILE:
		savePath = baseBasePath + "files/"
	case enum.FILE_TYPE_VIDEO:
		savePath = baseBasePath + "videos/"
	case enum.FILE_TYPE_VOICES:
		savePath = baseBasePath + "voices/"
	case enum.FILE_TYPE_OTHER:
		savePath = baseBasePath + "others/"
	default:
		savePath = baseBasePath + "images/"
		thumbnailPath = baseBasePath + "thumbnail/"
	}

	if len(this.SavePath) > 0 {
		savePath += strings.TrimLeft(this.SavePath, "/")
	} else {
		year, month, day := time.Now().Date()
		url := strconv.Itoa(year) + "-" + strconv.Itoa(int(month)) + "/" + strconv.Itoa(day)
		savePath += url
	}
	if "image" == this.UploadType && this.IsThumbnail {
		year, month, day := time.Now().Date()
		url := strconv.Itoa(year) + "-" + month.String() + "/" + strconv.Itoa(day)
		thumbnailPath += url
	}
}

func (this *UpFile) setFileName() {
	if this.IsRename {
		fileName = GenerateRandomString(16) + "." + GetExt(this.Headers.Filename)
	} else {
		fileName = this.Headers.Filename
	}
}

func (this *UpFile) checkExt() error {
	ext := GetExt(this.Headers.Filename)
	if !strings.Contains(allowExts, ext) {
		return errors.New("不允许的文件后缀名：" + ext)
	}
	return nil
}

func (this *UpFile) checkFileSize() error {
	if this.Headers.Size > allowSize {
		return errors.New("上传文件限制大小为：" + HumanSize(allowSize))
	}
	return nil
}

func (this *UpFile) checkPath() error {
	_, err := os.Stat(savePath)
	if err != nil && os.IsNotExist(err) {
		if err := os.MkdirAll(savePath, 0755); err != nil {
			return errors.New("创建目录失败。path: " + savePath + ", errMsg: " + err.Error())
		}
	}
	return nil
}

func MakeThumbnail(filename string) string {

	url := ""
	return url
}

/*
	TODO:

GetImageInfo 获取图片信息（宽、高）
filename 文件路径
*/
func GetImageInfo(filename string) map[string]int {

	imgInfo := map[string]int{
		"width": 0,
		"hight": 0,
	}
	return imgInfo
}

/*
	TODO:

GetVideoInfo 获取视频信息（宽、高、时长、首帧图片）
filename 文件路径
*/
func GetVideoInfo(filename string) map[string]interface{} {

	videoInfo := map[string]interface{}{
		"width":          0,
		"hight":          0,
		"duration":       "00:00:00",
		"firt_frame_url": "",
	}
	return videoInfo
}

/**
 * ScanDir
 * @Description: 获取目录下的所有文件(不包含子级)
 * @param dirPath 目录路径
 * @param isIncludeDir 返回结果中是否包含目录名称
 * @return error
 * @return []string
 */
func ScanDir(dirPath string, isIncludeDir bool) (error, []string) {
	files := make([]string, 0, 0)
	if len(dirPath) == 0 {
		return errors.New(lang.CodeMessage(6)), files
	}
	if fileinfo, err := os.Stat(dirPath); err != nil {
		if os.IsNotExist(err) {
			return errors.New(lang.CodeMessage(10006) + ", path: " + dirPath), files
		}
		return err, files
	} else {
		if !fileinfo.IsDir() {
			return errors.New(lang.CodeMessage(10005) + ", path: " + dirPath), nil
		}
	}
	if des, err := os.ReadDir(dirPath); err != nil {
		return err, files
	} else {
		for _, file := range des {
			name := file.Name()
			if name == "." || name == ".." {
				continue
			}
			if !isIncludeDir && file.IsDir() {
				continue
			}
			files = append(files, file.Name())
		}
	}
	return nil, files
}

/*
MakeFileUUID 生成文件指纹
filename 文件路径
*/
func MakeFileUUID(filename string) string {
	return GenerateFileUUID(filename, "md5")
}

/*
GetExt 获取文件后缀名
filename 文件路径
*/
func GetExt(filename string) string {
	filename = path.Ext(path.Base(filename))
	ext := strings.TrimLeft(filename, ".")
	return ext
}

/*
HumanSize 把字节单位转换为 Kb、Mb、Gb 等人性化格式
fileSize 文件大小，单位字节
*/
func HumanSize(fileSize int64) (size string) {
	if fileSize > 1024 {
		return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
	} else if fileSize > (1024 * 1024) {
		return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
	} else if fileSize > (1024 * 1024 * 1024) {
		return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
	} else if fileSize > (1024 * 1024 * 1024 * 1024) {
		return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
	} else if fileSize > (1024 * 1024 * 1024 * 1024 * 1024) {
		return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
	} else {
		return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
	}
}

/**
*  WriteToFile
*  @Description: 把一个字符串追加写入到指定文件
*  @param filename 文件名
*  @param msg 要写入的字符串
*  @return error
 */
func WriteToFile(filename string, msg string) error {
	filePath := path.Dir(filename)
	if err := MkDir(filePath); err != nil {
		return err
	}

	var mutex sync.Mutex
	// 互斥加锁
	mutex.Lock()
	// 以指定的权限打开文件
	file, _ := os.OpenFile(filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0664)
	defer func() {
		_ = file.Close()
		// 解锁
		mutex.Unlock()
	}()
	// 写入字节
	if _, err := file.Write([]byte(msg)); err != nil {
		return err
	}
	// 确保写入到磁盘
	return file.Sync()
}

/**
*  WriteLog
*  @Description: 手动写入日志到指定文件文件（）用于需要手动指定文件的场景
*  @param filename 文件名
*  @param msg 日志消息
*  @param level 日志级别，error、warning、debug、info 等
*  @return error
 */
func WriteLog(filename string, msg string, level string) error {
	filePath := path.Dir(filename)
	baseFileName := path.Base(filename)
	baseFileName = level + "-" + baseFileName
	logMsg := "[" + time.Now().Format("2006-01-02 15:04:05") + "]" + "[" + level + "] " + msg + "\n"
	return WriteToFile(filePath+"/"+baseFileName, logMsg)
}

/**
*  ReadFileContent
*  @Description: 读取文件内容
*  @param filename 文件名（文件路径）
*  @return error
*  @return string 文件内容
 */
func ReadFileContent(filename string, isRemoteFile bool) (error, string) {
	var content string
	if isRemoteFile {
		resp, httpErr := http.Get(filename)
		if httpErr != nil {
			return errors.New(httpErr.Error()), content
		}
		defer func() {
			_ = resp.Body.Close()
		}()
		// 获得get请求响应的reader对象
		reader := bufio.NewReaderSize(resp.Body, 32*1024)
		contentBytes := make([]byte, 32*1024)
		for {
			conBytes := make([]byte, 32*1024)
			byteLength, err := reader.Read(conBytes)
			if err != nil {
				if err == io.EOF {
					break
				}
				return errors.New(err.Error()), content
			}

			contentBytes = append(contentBytes, conBytes[0:byteLength]...)
		}
		content = string(contentBytes)
	} else {
		if _, err := os.Stat(filename); err != nil {
			if os.IsNotExist(err) {
				return errors.New("文件不存在"), content
			} else {
				return err, content
			}
		}
		file, openErr := os.Open(filename)
		if openErr != nil {
			return errors.New(openErr.Error()), content
		}
		defer func() {
			_ = file.Close()
		}()
		if val, err := io.ReadAll(file); err != nil {
			return errors.New(err.Error()), content
		} else {
			content = string(val)
		}
	}
	return nil, content
}

/**
 * MkDir
 * @Description: 新建目录，默认递归创建
 * @param dirPath 目录路径
 * @return error
 */
func MkDir(dirPath string) error {
	if _, err := os.Stat(dirPath); err != nil {
		if os.IsNotExist(err) {
			if err := os.MkdirAll(dirPath, 0755); err != nil {
				return errors.New("创建目录失败。path: " + dirPath + ", errMsg: " + err.Error())
			}
			return nil
		}
		return err
	}
	return nil
}

func FileIsExists(filename string) (bool, error) {
	if _, err := os.Stat(filename); err != nil {
		return false, err
	} else {
		// 文件已经存在
		return true, nil
	}
}

func Unlink(filename string) error {
	return os.Remove(filename)
}

//
// DownloadRemoteFile
//  @Description: 下载远程文件到本地
//  @param filename 本地保存的文件名
//  @param url 文件的远程地址
//  @return error
//
func DownloadRemoteFile(filename string, url string) error {
	filePath := path.Dir(filename)
	if err := MkDir(filePath); err != nil {
		return err
	}
	resp, httpErr := http.Get(url)
	if httpErr != nil {
		return httpErr
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer func() {
		_ = file.Close()
	}()
	if _, err := io.Copy(file, resp.Body); err != nil {
		return err
	}
	return nil
}
